package app

import (
	"encoding/json"
	"golang-blog/helper"
	"golang-blog/infrastructure/log"
	"golang-blog/models"

	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
)

// 关于文章的操作  相当于controller层

// 文章新增
type ArticleAddReq struct {
	CategoryName    string   `json:"category_name"`   // 分类名
	ParentCategory  string   `json:"parent_category"` // 父分类名
	CateDescription string   `json:"category_desc"`
	Status          int      `json:"status"` // 0-草稿 1-发表  -1 失效
	Kind            int      `json:"kind"`   //类型1-文章，2-页面
	Title           string   `json:"title"`
	URLPath         string   `json:"url_path"`
	Summary         string   `json:"summary"` //摘要
	Content         string   `json:"content"`
	Img             string   `gorm:"type:varchar(100)" json:"img"` // 文章封面图片
	AllowCommnet    bool     `json:"allow"`                        // 是否允许评论
	Tags            []string `json:"tags"`                         // 标签
}

// 查询特定文章详情
type ArticleInfoQueryReq struct {
	ID    int64  `json:"id"`    // 主键  ID
	Title string `json:"title"` // 标题，一般也是唯一的，可以根据标题查询；可以支持模糊查询
}
type ArticleInfoQueryResp struct {
	// gorm.Model
	ID int64 `json:"id"` // 主键ID
	// -------------------------------------------------------
	Title        string `json:"title"`         // 标题
	CategoryName string `json:"category_name"` //  类别详情
	Status       int    `json:"status"`        //1-草稿;2-已发布'
	Kind         int    `json:"kind"`          //类型1-文章，2-页面
	Summary      string `json:"summary"`       //摘要
	Content      string `json:"content"`       // 具体内容
	Img          string `json:"img"`           // 文章封面图片
	ReadCount    int    `json:"read_count"`    // 阅读次数
	AllowCommnet bool   `json:"allow"`         // 是否允许评论
	CommentCount int    `json:"comment_count"` // 评论数量
}
type ArticleListQueryReq struct {
	CategoryName string `json:"category_name"` // 查询某个类别下所有的文章; 可以为空
	Page         int    `json:"page"`          // 用于分页
	PageSize     int    `json:"page_size"`     // limit
}
type ArticleListQueryResp struct {
	Articles     []ArticleInfoQueryResp `json:"article_list"`   // 文章列表
	Page         int                    `json:"page"`           //当前页索引，从1开始计数
	Count        int                    `json:"count"`          //每页的实际记录条数，可能小于需要获取的条数
	TotalPageNum int                    `json:"total_page_num"` //总页数
}

// 更新
type ArticleUpdateReq struct {
	ID              int      `json:"id"`
	CategoryName    string   `json:"category_name"`   // 分类名
	ParentCategory  string   `json:"parent_category"` // 父分类名
	CateDescription string   `json:"category_desc"`
	Status          int      `json:"status"` // 0-草稿 1-发表  -1 失效
	Kind            int      `json:"kind"`   //类型1-文章，2-页面
	Title           string   `json:"title"`
	URLPath         string   `json:"url_path"`
	Summary         string   `json:"summary"` //摘要
	Content         string   `json:"content"`
	Img             string   `gorm:"type:varchar(100)" json:"img"` // 文章封面图片
	AllowCommnet    bool     `json:"allow"`                        // 是否允许评论
	Tags            []string `json:"tags"`                         // 标签
}

// 删除
type ArticleDelteReq struct {
	ID int `json:"id"`
}

func NewArticle() gin.HandlerFunc {
	return func(c *gin.Context) {
		aar := new(ArticleAddReq)
		if err := c.ShouldBindJSON(aar); err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeValidateFail, helper.Translate(err))
			return
		}
		// 一些必要的检查
		// 1. 判断类别是否已经存在，若存在，查询得到类别ID，以及其父类别ID；否则创建
		_, err := models.NewCategory(nil).Where(&models.Category{
			CategoryName:   aar.CategoryName,
			ParentCategory: aar.ParentCategory,
		}).FirstOrCreate()
		if err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "category firstorcreate fail").Error())
			return
		}
		// 2. 插入文章
		// TODO: uniqueIdx title；但尽管插入失败，仍消耗一个id
		rowId, err := models.NewArticle(nil).With(&models.Article{
			Title:        aar.Title,
			Status:       aar.Status,
			Kind:         aar.Kind,
			URLPath:      aar.URLPath,
			Summary:      aar.Summary,
			Content:      aar.Content,
			AllowCommnet: aar.AllowCommnet,
			// Cid:          arc.ID, // 得到外键ID
			CategoryName: aar.CategoryName,
			// Category: models.Category{
			// 	CategoryName:   aar.CategoryName,
			// 	Description:    aar.CateDescription,
			// 	ParentCategory: aar.ParentCategory,
			// },
		}).Create()
		if err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "db insert fail").Error())
			return
		}
		// 3. 更新标签   文章-标签关系
		log.Infof("Tags: %v", aar.Tags)
		for _, tag := range aar.Tags {
			// 创建tag
			// TODO: 如何一次调用，完成创建和tag引用次数++;  Scope
			t, err := models.NewTag(nil).Where(&models.Tag{
				TagName: tag,
			}).FirstOrCreate()
			if err != nil {
				log.Infof("new tag err: %v", err)
				helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "db firstOrCreate tag fail").Error())
				return
			}

			// 更新 或  新建  post-tag关系
			_, err = models.NewPostTag(nil).Where(&models.PostTag{
				PostId: int(rowId),
				TagId:  t.ID,
			}).FirstOrCreate()
			if err != nil {
				log.Infof("new post-tag err: %v", err)
				helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "db firstOrCreate post-tag fail").Error())
				return
			}
		}
		resp := map[string]interface{}{
			"row_id": rowId,
		}
		data, _ := json.Marshal(resp)
		helper.SetHttpDataResp(c, string(data))
	}
}

// 返回文章列表
func GetArticleList() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 分页查询
		alq := new(ArticleListQueryReq)
		if err := c.ShouldBindJSON(alq); err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeValidateFail, helper.Translate(err))
			return
		}
		arcList, count, totalPages, err := models.NewArticle(nil).Where(&models.Article{
			CategoryName: alq.CategoryName}).QueryRange(alq.Page, alq.PageSize)
		if err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "db query fail").Error())
			return
		}
		// 构建response
		arcs := []ArticleInfoQueryResp{}
		for _, post := range *arcList {
			aqr := ArticleInfoQueryResp{
				ID:           post.ID,
				Title:        post.Title,
				CategoryName: post.CategoryName,
				Status:       post.Status,
				Kind:         post.Kind,
				Summary:      post.Summary,
				Content:      post.Content,
				Img:          post.Img,
				ReadCount:    post.ReadCount,
				AllowCommnet: post.AllowCommnet,
				CommentCount: post.CommentCount,
			}
			arcs = append(arcs, aqr)
		}
		resp := ArticleListQueryResp{
			Articles:     arcs,
			Page:         alq.Page,
			Count:        count,      // 记录总数
			TotalPageNum: totalPages, // 总页数
		}
		data, _ := json.Marshal(resp)
		helper.SetHttpDataResp(c, string(data))
	}
}

// 获取某个文章
func GetArticle() gin.HandlerFunc {
	return func(c *gin.Context) {
		aiq := new(ArticleInfoQueryReq)
		if err := c.ShouldBindJSON(aiq); err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeValidateFail, helper.Translate(err))
			return
		}
		// 查询
		arc, err := models.NewArticle(nil).Where(&models.Article{
			// ID: aiq.ID,
			Title: aiq.Title,
		}).First()
		if err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "db query fail").Error())
			return
		}
		resq := ArticleInfoQueryResp{
			ID:           arc.ID,
			Title:        arc.Title,
			CategoryName: arc.CategoryName,
			Status:       arc.Status,
			Kind:         arc.Kind,
			Summary:      arc.Summary,
			Content:      arc.Content,
			Img:          arc.Img,
			ReadCount:    arc.ReadCount,
			AllowCommnet: arc.AllowCommnet,
			CommentCount: arc.CommentCount,
		}
		data, _ := json.Marshal(resq)
		helper.SetHttpDataResp(c, string(data))
	}
}

// 更新操作
func UpdateArticle() gin.HandlerFunc {
	return func(c *gin.Context) {
		aur := new(ArticleUpdateReq)
		if err := c.ShouldBindJSON(aur); err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeValidateFail, helper.Translate(err))
			return
		}
		// 一些必要的检查
		// 1. 判断类别是否已经存在，若存在，查询得到类别ID，以及其父类别ID；否则创建
		// TODO: 如果存在，则应该更新
		cat := models.Category{
			CategoryName:   aur.CategoryName,
			ParentCategory: aur.ParentCategory,
			Description:    aur.CateDescription,
		}
		arc, err := models.NewCategory(nil).With(&cat).Where(&cat).FirstOrCreate()
		if err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "category firstorcreate fail").Error())
			return
		}
		// 2. 更新文章
		err = models.NewArticle(nil).With(&models.Article{
			Title:        aur.Title,
			Status:       aur.Status,
			Kind:         aur.Kind,
			URLPath:      aur.URLPath,
			Summary:      aur.Summary,
			Content:      aur.Content,
			AllowCommnet: aur.AllowCommnet,
			CategoryName: aur.CategoryName,
		}).Where(&models.Article{ID: int64(aur.ID)}).Update()
		if err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "db update fail").Error())
			return
		}
		// 3. 更新标签   文章-标签关系
		for _, tag := range aur.Tags {
			// 更新或创建tag
			t, err := models.NewTag(nil).Where(&models.Tag{
				TagName: tag,
			}).FirstOrCreate()
			if err != nil {
				log.Infof("new tag err: %v", err)
				helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "db firstOrCreate tag fail").Error())
				return
			}
			// 更新 或  新建  post-tag关系
			_, err = models.NewPostTag(nil).Where(&models.PostTag{
				PostId: arc.ID,
				TagId:  t.ID,
			}).FirstOrCreate()
			if err != nil {
				log.Infof("new post-tag err: %v", err)
				helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "db firstOrCreate post-tag fail").Error())
				return
			}
		}
		helper.SetHttpMsgResp(c, helper.HttpCodeOK, "success")
	}
}

// 删除某篇文章：硬删除
func DeleteArticle() gin.HandlerFunc {
	return func(c *gin.Context) {
		adr := new(ArticleDelteReq)
		if err := c.ShouldBindJSON(adr); err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeValidateFail, helper.Translate(err))
			return
		}
		// 一些必要的检查
		// 1. 检索到文章信息
		arc, err := models.NewArticle(nil).Where(&models.Article{ID: int64(adr.ID)}).First()
		if err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "article query fail").Error())
			return
		}
		// 2. 删除文章
		err = models.NewArticle(nil).Where(arc).Delete()
		if err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "article delete fail").Error())
			return
		}
		// 3. 删除post-tag数据
		// 3.1 更新post-tag；有可能更新tag
		// TODO: 批量删除，AfterDelete钩子的调用会如何，是调用n次，还是调用1次
		err = models.NewPostTag(nil).Where(&models.PostTag{PostId: adr.ID}).Delete()
		if err != nil {
			helper.SetHttpMsgResp(c, helper.HttpCodeInternalFail, errors.WithMessage(err, "post-tag delete fail").Error())
			return
		}
		helper.SetHttpMsgResp(c, helper.HttpCodeOK, "success")
	}
}
